15. 【选修】练习:迭代器和生成器

练习:实现 my_enumerate

请自己写一个效果和内置函数 enumerate 一样的生成器函数。

如下所示地调用该函数:

lessons = ["Why Python Programming", "Data Types and Operators", "Control Flow", "Functions", "Scripting"]

for i, lesson in my_enumerate(lessons, 1):
    print("Lesson {}: {}".format(i, lesson))

应该会输出:

Lesson 1: Why Python Programming
Lesson 2: Data Types and Operators
Lesson 3: Control Flow
Lesson 4: Functions
Lesson 5: Scripting

Start Quiz:

lessons = ["Why Python Programming", "Data Types and Operators", "Control Flow", "Functions", "Scripting"]

def my_enumerate(iterable, start=0):
    # Implement your generator function here


for i, lesson in my_enumerate(lessons, 1):
    print("Lesson {}: {}".format(i, lesson))

为何要使用生成器?

你可能会疑问,为何要使用生成器,而不使用列表。下面这段摘自 stack overflow 页面 的内容回答了这个问题:

生成器是构建迭代器的 “懒惰” 方式。当内存不够存储完整实现的列表时,或者计算每个列表元素的代价很高,你希望尽量推迟计算时,就可以使用生成器。但是这些元素只能遍历一次。

另一种详细的解释如下(详细说明参见 该 stack overflow 页面。)

由于使用生成器是一次处理一个数据,在内存和存储的需求上会比使用 list 方式直接全部生成再存储节省很多资源。

由此区别,在处理大量数据时,经常使用生成器初步处理数据后,再进行长期存储,而不是使用 list。因为无论使用生成器还是 list,都是使用过就要丢弃的临时数据。既然功能和结果一样,那就不如用生成器。

但是生成器也有自己的局限,它产生的数据不能回溯,不像 list 可以任意选择。

练习:Chunker

如果可迭代对象太大,无法完整地存储在内存中(例如处理大型文件时),每次能够使用一部分很有用。

实现一个生成器函数 chunker,接受一个可迭代对象并每次生成指定大小的部分数据。

如下所示地调用该函数:

for chunk in chunker(range(25), 4):
    print(list(chunk))

应该会输出:

[0, 1, 2, 3]
[4, 5, 6, 7]
[8, 9, 10, 11]
[12, 13, 14, 15]
[16, 17, 18, 19]
[20, 21, 22, 23]
[24]

Start Quiz:

def chunker(iterable, size):
    # Implement function here


for chunk in chunker(range(25), 4):
    print(list(chunk))